
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>PatrolJS Demo</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	</head>

	<style>
		body {
			font-family: Arial;
			background-color: #000;
			color: #fff;
			margin: 0px;
			overflow: hidden;
		}

		.info {
			padding: 10px;
			width: 100%;
			position: absolute;
			background-color: rgba(255, 0, 0, 0.5);
			font-size: 14px;
		}

		.info h1 {
			padding: 0;
			margin: 0;
		}

	</style>

	<body>

		<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.js"></script>
		<script src="OrbitControls.js"></script>
		<script src="../patrol.js"></script>

		<div class="info">
			<h1>PatrolJS demo</h1>
			<a href="https://github.com/nickjanssen/PatrolJS">https://github.com/nickjanssen/PatrolJS</a>
			<p>Click anywhere on the level to calculate a path. Click and drag to rotate the level.</p>
		</div>

		<script>

			var container;

			var camera, scene, renderer, controls;

			var raycaster, intersectedObject;

			var mouse = new THREE.Vector2();

			var startTime	= Date.now();

			var windowHalfX = window.innerWidth / 2;
			var windowHalfY = window.innerHeight / 2;

			var lastFrameTime = 0;
			var maxFrameTime = 0.03;
			var elapsedTime = 0;

			var level;

			init();
			animate();

			var player, target;

			var playerNavMeshGroup;

			var calculatedPath = null;

			var pathLines;

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
				camera.position.x = -10;
				camera.position.y = 14;
				camera.position.z = 10;

				scene = new THREE.Scene();

				var ambient = new THREE.AmbientLight( 0x101030 );
				scene.add( ambient );

				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
				directionalLight.position.set( 0, 0.5, 0.5 );
				scene.add( directionalLight );

  				var jsonLoader = new THREE.JSONLoader();

		        jsonLoader.load( 'meshes/level.js', function( geometry, materials ) {
		        	level = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
		        	scene.add(level);
		        }, null);

		        jsonLoader.load( 'meshes/level.nav.js', function( geometry, materials ) {

					var zoneNodes = patrol.buildNodes(geometry);

					patrol.setZoneData('level', zoneNodes);

		        	var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
		        		color: 0xd79fd4,
		        		opacity: 0.5,
		        		transparent: true
		        	}));

		        	scene.add(mesh);

		        	// Set the player's navigation mesh group
		        	playerNavMeshGroup = patrol.getGroup('level', player.position);

		        }, null);

		        // Add test sphere
				var geometry = new THREE.SphereGeometry( 0.25, 32, 32 );
				var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
				player = new THREE.Mesh( geometry, material );
				scene.add( player );

				player.position.set(-3.5, 0.5, 5.5);

				geometry = new THREE.BoxGeometry( 0.3, 0.3, 0.3 );
				var material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
				target = new THREE.Mesh( geometry, material );
				scene.add( target );

				target.position.copy(player.position);

				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.setClearColor(0xffffff);
				container.appendChild( renderer.domElement );

				raycaster = new THREE.Raycaster();

				document.addEventListener( 'click', onDocumentMouseClick, false );
				window.addEventListener( 'resize', onWindowResize, false );

				controls = new THREE.OrbitControls( camera );
				controls.damping = 0.2;

			}

			function onDocumentMouseClick (event) {

				// event.preventDefault();

				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

				camera.updateMatrixWorld();

				raycaster.setFromCamera( mouse, camera );

				var intersects = raycaster.intersectObject( level );

				if ( intersects.length > 0 ) {
					var vec = intersects[0].point;
					target.position.copy(vec);

					// Calculate a path to the target and store it
					calculatedPath = patrol.findPath(player.position, target.position, 'level', playerNavMeshGroup);

					if (calculatedPath && calculatedPath.length) {

						if (pathLines) {
							scene.remove(pathLines);
						}

						var material = new THREE.LineBasicMaterial({
							color: 0x0000ff,
							linewidth: 2
						});

						var geometry = new THREE.Geometry();
						geometry.vertices.push(player.position);

						// Draw debug lines
						for (var i = 0; i < calculatedPath.length; i++) {
							geometry.vertices.push(calculatedPath[i].clone().add(new THREE.Vector3(0, 0.2, 0)));
						}

						pathLines = new THREE.Line( geometry, material );
						scene.add( pathLines );

						// Draw debug cubes except the last one. Also, add the player position.
						var debugPath = [player.position].concat(calculatedPath);

						for (var i = 0; i < debugPath.length - 1; i++) {
							geometry = new THREE.BoxGeometry( 0.3, 0.3, 0.3 );
							var material = new THREE.MeshBasicMaterial( {color: 0x00ffff} );
							var node = new THREE.Mesh( geometry, material );
							node.position.copy(debugPath[i]);
							pathLines.add( node );
						}
					}
				}
			}

			function onWindowResize() {
				windowHalfX = window.innerWidth / 2;
				windowHalfY = window.innerHeight / 2;

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );
			}

			function animate() {
                var currTime = window.performance.now();
                var delta = (currTime - lastFrameTime) / 1000;
                var dTime = Math.min(delta, maxFrameTime);
                elapsedTime += delta;
                lastFrameTime = currTime;

                tick(dTime);

				requestAnimationFrame( animate );
				render();
			}

			function tick(dTime) {
				if (!level) {
					return;
				}

				var speed = 5;

				var targetPosition;

				if (calculatedPath && calculatedPath.length) {
					targetPosition = calculatedPath[0];

					var vel = targetPosition.clone().sub(player.position);

					if (vel.lengthSq() > 0.05 * 0.05) {
						vel.normalize();

						// Mve player to target
						player.position.add(vel.multiplyScalar(dTime * speed));
					}
					else {
						// Remove node from the path we calculated
						calculatedPath.shift();
					}
				}
			}

			function render() {
				camera.lookAt( scene.position );
				camera.updateMatrixWorld();

				renderer.render( scene, camera );

			}

		</script>

<a href="https://github.com/nickjanssen/PatrolJS"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>

	</body>
</html>
